package com.aiwiown.face;

import com.aiwiown.face.internal.mapping.Converters;
import com.aiwiown.face.internal.parser.json.ObjectJsonParser;
import com.aiwiown.face.internal.parser.xml.ObjectXmlParser;
import com.aiwiown.face.internal.util.ApiHashMap;
import com.aiwiown.face.internal.util.ApiLogger;
import com.aiwiown.face.internal.util.StringUtils;
import com.aiwiown.face.internal.util.WebUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName : AbstractApiClient
 * @Description :
 * @Author : dbin0123
 * @Date: 2020-03-13 11:57
 */
public abstract class AbstractApiClient implements ApiClient {
    private String serverUrl;
    private String appId;
    private String charset;
    private String appSecret;
    private Integer readOutTime;
    private Integer connectTimeOut;
    private String format = ApiConstants.Format.JSON.getCode();

    protected AbstractApiClient() {
    }

    public AbstractApiClient(String serverUrl, String appId, String appSecret) {
        this.serverUrl = serverUrl;
        this.appId = appId;
        this.appSecret = appSecret;
        this.charset = ApiConstants.Charset.UTF8.getCode();
        this.readOutTime = ApiConstants.READ_OUT_TIME;
        this.connectTimeOut = ApiConstants.CONNECT_TIME_OUT;
    }

    public AbstractApiClient(String serverUrl, String appId, String appSecret, String charset) {
        this.appId = appId;
        this.charset = charset;
        this.serverUrl = serverUrl;
        this.appSecret = appSecret;
        this.readOutTime = ApiConstants.READ_OUT_TIME;
        this.connectTimeOut = ApiConstants.CONNECT_TIME_OUT;
    }

    /**
     * 请求方法
     *
     * @param request
     * @param <T>
     * @return
     * @throws ApiException
     */
    @Override
    public <T extends ApiResponse> T execute(ApiRequest<T> request) throws ApiException {
        try {
            ApiParser<T> parser = null;
            if (ApiConstants.Format.XML.getCode().equals(this.format)) {
                parser = new ObjectXmlParser<T>(request.getResponseClass());
            } else {
                parser = new ObjectJsonParser<T>(request.getResponseClass());
            }
            return _execute(request, parser);
        } catch (Exception e) {
            throw new ApiException(e);
        }
    }

    private <T extends ApiResponse> T _execute(ApiRequest<T> request, ApiParser<T> parser) throws Exception {
        long beginTime = System.currentTimeMillis();
        Map<String, Object> rt = request(request);
        if (rt == null) {
            return null;
        }
        Map<String, Long> costTimeMap = new HashMap<String, Long>();
        if (rt.containsKey("prepareTime")) {
            costTimeMap.put("prepareCostTime", (Long) (rt.get("prepareTime")) - beginTime);
            if (rt.containsKey("requestTime")) {
                costTimeMap.put("requestCostTime", (Long) (rt.get("requestTime")) - (Long) (rt.get("prepareTime")));
            }
        }
        T tRsp = null;
        try {
            // 若需要解密则先解密
            ResponseItem responseItem = decryptResponse(request, rt, parser);
            // 解析实际串
            tRsp = parser.parse(responseItem.getRealContent());
            tRsp.setBody(responseItem.getRealContent());
            if (costTimeMap.containsKey("requestCostTime")) {
                costTimeMap.put("postCostTime", System.currentTimeMillis() - (Long) (rt.get("requestTime")));
            }
        } catch (RuntimeException e) {
            ApiLogger.logBizError((String) rt.get("rsp"), costTimeMap);
            throw e;
        } catch (ApiException e) {
            ApiLogger.logBizError((String) rt.get("rsp"), costTimeMap);
            throw new ApiException(e);
        }
        tRsp.setParams((ApiHashMap) rt.get("textParams"));
        if (!tRsp.isSuccess()) {
            ApiLogger.logErrorScene(rt, tRsp, "", costTimeMap);
            tRsp.build();
        } else {
            ApiLogger.logBizSummary(rt, tRsp, costTimeMap);
        }
        return tRsp;
    }

    private <T extends ApiResponse> ResponseItem decryptResponse(ApiRequest<T> request, Map<String, Object> rt, ApiParser<T> parser) {
        String responseBody = (String) rt.get("realContent");
        String realBody = (String) rt.get("realContent");
        return new ResponseItem(responseBody, realBody);
    }


    /**
     * request请求
     *
     * @return
     * @throws Exception
     */
    private <T extends ApiResponse> Map<String, Object> request(ApiRequest<T> request) throws Exception {
        Map<String, Object> result = new HashMap<String, Object>();
        String requestUrl = this.serverUrl + request.getApiUrl();
        Map<String, String> textParams = request.getTextParams();
        String signVersion = request.signVersion();
        if (StringUtils.isEmpty(signVersion)) {
            textParams.put("api_key", this.appId);
            textParams.put("api_secret", this.appSecret);
        } else {
            textParams.put("sign_version", signVersion);
            textParams.put("sign", request.getSignInfo(this.appId, this.appSecret));
        }
        Map<String, byte[]> byteArrayParams = request.getByteArrayParams();
        result.put("prepareTime", System.currentTimeMillis());
        String realContent = new String(WebUtils.request(requestUrl, request.getApiMethod(), textParams, byteArrayParams, this.connectTimeOut, this.readOutTime, this.charset));
        result.put("realContent", Converters.unicodeToString(realContent));
        result.put("requestTime", System.currentTimeMillis());
        result.put("url", requestUrl);
        return result;
    }

    public void setFormat(String format) {
        this.format = format;
    }

    public void setReadOutTime(Integer readOutTime) {
        this.readOutTime = readOutTime;
    }

    public void setConnectTimeOut(Integer connectTimeOut) {
        this.connectTimeOut = connectTimeOut;
    }
}
